Skip to content

Bump yandex_ynison to v2.2.9: handoff state-sync FSM and opt-in stream-mode UI integration#3856

Open
trudenboy wants to merge 14 commits into
music-assistant:devfrom
trudenboy:upstream/yandex_ynison
Open

Bump yandex_ynison to v2.2.9: handoff state-sync FSM and opt-in stream-mode UI integration#3856
trudenboy wants to merge 14 commits into
music-assistant:devfrom
trudenboy:upstream/yandex_ynison

Conversation

@trudenboy
Copy link
Copy Markdown
Contributor

@trudenboy trudenboy commented May 8, 2026

Summary

Bump yandex_ynison plugin provider to v2.2.9. The diff against dev introduces a real two-way state-sync layer for the experimental handoff playback mode and adds an opt-in stream-mode UI integration so MA's player card renders fully while a PluginSource is active.

Source: trudenboy/ma-provider-yandex-ynison@v2.2.9.

Highlights

Handoff state-sync

Two-way sync between the Yandex Music app and MA's player queue is driven by an explicit HandoffPhase FSM (IDLE / ACTIVATING / PLAYING / PAUSED / ENDING).

  • Centralised dispatcher: _handoff_activate_apply_track_change / _apply_idle_resume / _apply_same_track_sync.
  • Idempotency cache (1 s TTL); _cancel_pending_play_media() cancels the in-flight play_media task before issuing a new one. The task is created via self.mass.create_task(...) so it is tracked by the MusicAssistant instance and auto-cancelled on stop/reload.
  • Pause / resume routed through cmd_pause / cmd_play (avoids the queue's _watch_pause watchdog dropping to IDLE on long pauses). The same-URI-paused cmd_play resume snapshots _drift_suppress_until / _expected_phase before the await and rolls them back on Exception, symmetric with the REPLACE-issuing branches; a failed resume no longer leaves the session stuck in ACTIVATING.
  • Same-URI resume goes via play_media(REPLACE) + seek wrapped in cmd_pause / seek / cmd_play (avoids the audible 0-then-jump).
  • _prefetch_format_for_track runs in the background (mass.create_task(...)) on track change instead of blocking play_media — the prefetch is cosmetic in handoff (only primes MA's stream-detail cache; _normalized_format is never used here), so the up-to-2.5 s latency per track change is gone.
  • Natural-end auto-advance: PLAYING → IDLE queue-state transition, gated on _expected_phase == PLAYING and _is_at_natural_end_of_track. Heartbeat-side polling (≤ 5 s) as a fallback when MA's event bus drops the transition.
  • Echo classification: author-only AND check on both queue.version.device_id and status.version.device_id. Lamport-style watermarks are intentionally not used — Ynison documents version.version as random(int64) and re-stamps it.
  • Reconnect settle window (2 s) drops the first inbound state after WS reconnect; _connect_state always sends a fresh initial state.
  • Shared helpers used by both stream + handoff: _classify_drift (queue-rebuild guard, prevents stream mode from treating a Ynison progress=0 echo as a seek), _pick_resume_position.
  • _handoff_pause echoes paused=True to Ynison immediately and bumps the heartbeat watermark so the next tick doesn't race the user's pause.

Stream-mode MA UI integration (opt-in)

New advanced config — enable_ui_integration (default off) — publishes a frontend-only fake queue under the provider's instance_id, so MA's UI renders the seek bar, signal-chain panel, and quality indicator while a PluginSource is active.

  • _register_plugin_queue fires QUEUE_ADDED / QUEUE_UPDATED events without touching player_queues._queues — backend command routing stays on the PluginSource callbacks.
  • _set_player_output_format stamps a real AudioFormat instance (not a plain dict) on player.extra_data["output_format"], matching MA core's contract (controllers/streams/audio.py:get_player_filter_params writes AudioFormat; DSPDetails.output_format: AudioFormat | None). Cleared in unload() and _clear_active_player.
  • _signal_seek_to_frontend snaps the frontend bar via a public QUEUE_TIME_UPDATED event (no private-attribute writes).
  • Queue state derived from the live YnisonState.is_paused flag so pause / resume reflect immediately.
  • Off by default because the integration relies on private frontend behaviours and can interfere with "Play Now" on local content while the source is active.
  • Multi-user limitation: MA's websocket gate (controllers/webserver/websocket_client.py:444-454) drops QUEUE_* events whose event.object_id is not in the user's player_filter. Our events use instance_id (matching player.active_source for the frontend's queue lookup), so users with a restricted player_filter see an empty player card. Switching object_id to player_id would pass the filter but break the lookup for everyone. A clean fix needs an MA-core carve-out for plugin source ids (mirroring the existing _sendspin_player_id exception) or a frontend change to route QUEUE_* by data.queue_id — both out of scope for this PR. Admin / unrestricted users are unaffected; the integration is opt-in.

Stream-mode reconnect resilience

  • _send_progress_to_ynison short-circuits when paused=False and the server-side queue is empty (current_track_id is None). Without this, every 5 s _sync_progress tick after a reconnect that lands on a stale empty-queue state fired update_playing_status(paused=False), which Ynison rejects with error 400030001 ("Player is not paused, but queue is probably empty") and closes the WebSocket — ping-ponging through reconnects (sometimes escalating into a 300100002 rebalance loop) for minutes. Observed live and root-caused. paused=True is still sent (one-sided guard; the server accepts that combination).

Config UX

  • playback_mode and Device name in Yandex Music (publish_name) moved out of the advanced section.
  • New advanced toggle enable_ui_integration (hidden in handoff mode).

Stats

10 files changed, ≈3000 insertions(+), ≈90 deletions(-) (vs dev). One new test file: tests/providers/yandex_ynison/test_provider_handoff.py (≈1000 lines). 285 unit tests, ruff and mypy clean on the source repo.

Test plan

  • Unit tests, ruff, mypy — green
  • Live-tested in both playback_mode: stream and playback_mode: handoff: track change, pause / resume (short and long), next / prev, seek (forward and backward), mid-track handoff from the Yandex Music app, natural-end auto-advance.
  • Stream-mode UI integration live-tested with multiple track formats (FLAC 44.1/16, 44.1/24, 48/24, AAC 44.1/16). Toggle on/off paths verified.
  • Reconnect cascade fix verified against the live 400030001 scenario (no more WS ping-pong on empty-queue post-reconnect states).

CI sync

Published via trudenboy/ma-provider-tools/reusable-sync-to-fork.yml (target upstream/yandex_ynison).

@trudenboy trudenboy changed the title feat(yandex_ynison): add yandex_ynison provider v2.1.0 feat(yandex_ynison): Yandex Ynison plugin provider v2.1.0 — Spotify Connect-equivalent for Yandex Music May 8, 2026
@trudenboy trudenboy changed the title feat(yandex_ynison): Yandex Ynison plugin provider v2.1.0 — Spotify Connect-equivalent for Yandex Music feat(yandex_ynison): bump to v2.1.0 — handoff FSM refactor + reliability fixes May 8, 2026
@trudenboy trudenboy changed the title feat(yandex_ynison): bump to v2.1.0 — handoff FSM refactor + reliability fixes Bump yandex_ynison provider to v2.1.0 May 8, 2026
@trudenboy trudenboy changed the title Bump yandex_ynison provider to v2.1.0 Bump yandex_ynison to v2.2.0: handoff FSM rewrite + opt-in stream-mode UI integration May 8, 2026
@trudenboy trudenboy marked this pull request as ready for review May 8, 2026 20:57
Copilot AI review requested due to automatic review settings May 8, 2026 20:57
@trudenboy trudenboy changed the title Bump yandex_ynison to v2.2.0: handoff FSM rewrite + opt-in stream-mode UI integration Bump yandex_ynison to v2.2.0: handoff state-sync FSM and opt-in stream-mode UI integration May 8, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the yandex_ynison provider to upstream v2.2.0, bringing in a handoff-mode FSM rewrite, improved echo/reconnect handling in the Ynison client, and an opt-in stream-mode UI integration that publishes a frontend-only “fake queue” for richer UI rendering.

Changes:

  • Reworks echo detection and reconnect behavior in YnisonClient (AND-based author checks + post-reconnect settle window + session params update).
  • Adds a handoff playback mode with explicit FSM bookkeeping, heartbeat progress reporting, and idempotency/REPLACE handling.
  • Adds opt-in stream-mode UI integration (fake queue events + output format stamping) and format prefetch/hinting to improve output format accuracy.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
music_assistant/providers/yandex_ynison/__init__.py Adds playback mode selection and UI integration / handoff heartbeat config entries; features vary by mode.
music_assistant/providers/yandex_ynison/constants.py Introduces new config keys and handoff tuning constants.
music_assistant/providers/yandex_ynison/protocols.py Extends the Yandex Music provider protocol with instance_id for instance-scoped URIs.
music_assistant/providers/yandex_ynison/provider.py Implements handoff FSM + heartbeat, stream-mode UI integration (fake queue), and format prefetch/hints.
music_assistant/providers/yandex_ynison/streaming.py Adjusts default lossless PCM params to 44.1kHz.
music_assistant/providers/yandex_ynison/ynison_client.py Adds reconnect settle logic, session param updates, and AND-based echo classification.
tests/providers/yandex_ynison/test_provider.py Updates/extends provider tests for prefetch + normalized format behavior.
tests/providers/yandex_ynison/test_provider_handoff.py New comprehensive test suite for handoff mode FSM/heartbeat/idempotency behavior.
tests/providers/yandex_ynison/test_streaming.py Updates streaming tests for 44.1kHz lossless defaults.
tests/providers/yandex_ynison/test_ynison_client.py Updates Ynison client tests for new echo + reconnect semantics.

Comment thread music_assistant/providers/yandex_ynison/provider.py Outdated
Copilot AI review requested due to automatic review settings May 12, 2026 08:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread music_assistant/providers/yandex_ynison/provider.py Outdated
@MarvinSchenkel
Copy link
Copy Markdown
Contributor

Could you please resolve the conflicts / copilot comments. Will have a look after that

@MarvinSchenkel MarvinSchenkel marked this pull request as draft May 13, 2026 10:41
@trudenboy trudenboy changed the title Bump yandex_ynison to v2.2.0: handoff state-sync FSM and opt-in stream-mode UI integration Bump yandex_ynison to v2.2.6: handoff state-sync FSM and opt-in stream-mode UI integration May 13, 2026
@trudenboy trudenboy marked this pull request as ready for review May 13, 2026 11:27
Copilot AI review requested due to automatic review settings May 13, 2026 11:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread music_assistant/providers/yandex_ynison/provider.py Outdated
Comment thread music_assistant/providers/yandex_ynison/provider.py Outdated
@trudenboy
Copy link
Copy Markdown
Contributor Author

Could you please resolve the conflicts / copilot comments. Will have a look after that

@MarvinSchenkel Done

Copilot AI review requested due to automatic review settings May 13, 2026 12:36
@trudenboy trudenboy changed the title Bump yandex_ynison to v2.2.6: handoff state-sync FSM and opt-in stream-mode UI integration Bump yandex_ynison to v2.2.8: handoff state-sync FSM and opt-in stream-mode UI integration May 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

music_assistant/providers/yandex_ynison/provider.py:1386

  • [PROBLEM] QUEUE_TIME_UPDATED is emitted with object_id=self.instance_id, which can be filtered out by the websocket server for users with player_filter restrictions (it expects player_ids as object_id for QUEUE_* events). To keep the seek bar/progress updating for restricted users, consider using object_id=player_id (and keep the fake queue id in the payload) or another approach that survives the server-side filter.
            if self._ui_integration_active:
                self.mass.signal_event(
                    EventType.QUEUE_TIME_UPDATED,
                    object_id=self.instance_id,
                    data=elapsed_ms // 1000,
                )

Comment thread music_assistant/providers/yandex_ynison/VERSION Outdated
Comment thread music_assistant/providers/yandex_ynison/provider.py
@trudenboy trudenboy changed the title Bump yandex_ynison to v2.2.8: handoff state-sync FSM and opt-in stream-mode UI integration Bump yandex_ynison to v2.2.9: handoff state-sync FSM and opt-in stream-mode UI integration May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants